Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hierarchical refactorings #41975

Merged
merged 20 commits into from
Dec 23, 2020

Conversation

jessetrinity
Copy link
Contributor

Fixes #35096

Adds refactorKind strings to the GetApplicableRefactorsRequest to allow editors to request a specific refactor action or set of refactors/actions. I list them here along with the old names:

"Add or remove braces in an arrow function"

"refactor.rewrite.arrow.braces.add"
"refactor.rewrite.arrow.braces.remove"

"Convert arrow function or function expression"

"refactor.rewrite.function.anonymous"
"refactor.rewrite.function.arrow"
"refactor.rewrite.function.named"

"Convert export"

"refactor.rewrite.export.named"
"refactor.rewrite.export.default"

"Convert import"

"refactor.rewrite.import.named"
"refactor.rewrite.import.namespace"

"Convert overload list to single signature"
"refactor.rewrite.function.overloadList"

"Convert parameters to destructured object"
"refactor.rewrite.parameters.toDestructured"

"Convert to template string"
"refactor.rewrite.string"

"Convert to optional chain expression"
"refactor.rewrite.expression.optionalChain"

"Extract Symbol"

"refactor.extract.constant"
"refactor.extract.function"

"Extract type"

"refactor.extract.type"
"refactor.extract.interface"
"refactor.extract.typedef"

"Generate 'get' and 'set' accessors"
"refactor.rewrite.property.generateAccessors"

"Infer function return type"
"refactor.rewrite.function.returnType"

"Move to a new file"
"refactor.move.newFile"

cc @mjbvz for input on the names. I intend to follow the VSCode/LSP style where it applies - others I made up.

getAvailableRefactors will now compare the requested refactorKind to the set of actions available to each action before checking for applicable refactors. This allows us to skip out on unnecessary work if a specific action was requested. If refactorKinds is undefined we check each refactor as usual.

A 0-index substring of a refactoKind should return every action that would complete the requested refactorKind. That is, if refactor.extract is requested, we should return all of

"refactor.extract.constant"
"refactor.extract.function"
"refactor.extract.type"
"refactor.extract.interface"
"refactor.extract.typedef"

The most specific refactorKind applicable should always be returned.

The current error behavior is to return all refactor actions available to a given refactor along with their error messages, and return no error messages if at least one refactor action is applicable. I did not intend to modify that here but it might be good to change it to return all actions that matched a request, applicable and erroring, if editors can support it.

Since I was modifying the returned actions and had to add some error messages I did some refactoring around those to make things more consistent/readable. The crux of the change is in refactorProvider.ts and protocol.ts.

@typescript-bot
Copy link
Collaborator

Thanks for the PR! It looks like you've changed the TSServer protocol in some way. Please ensure that any changes here don't break consumers of the current TSServer API. For some extra review, we'll ping @sheetalkamat, @amcasey, @mjbvz, @minestarks for you. Feel free to loop in other consumers/maintainers if necessary

Copy link
Member

@PranavSenthilnathan PranavSenthilnathan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some minor nits. Looks good otherwise.

*/
export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean {
if(!requested) return true;
return known.substr(0, requested.length) === requested;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return known.substr(0, requested.length) === requested;
return known.startsWith(requested);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I probably can't change this?
image

* Checks if some refactor info has refactor error info.
*/
export function isRefactorErrorInfo(info: unknown): info is RefactorErrorInfo {
return (info as RefactorErrorInfo).error !== undefined;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (info as RefactorErrorInfo).error !== undefined;
return 'error' in info;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I wanted to do, but for some reason we forbid it though a lint rule:
Error: C:\Repos\TypeScript-GH\src\services\refactors\helpers.ts:14:16 no-in-operator Don't use the 'in' keyword - use 'hasProperty' to check for key presence instead

src/services/codefixes/generateAccessors.ts Outdated Show resolved Hide resolved
src/services/types.ts Outdated Show resolved Hide resolved
src/harness/fourslashImpl.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@mjbvz mjbvz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this. The names look good to me and the behavior you describe sounds correct to me

src/server/protocol.ts Outdated Show resolved Hide resolved
Copy link
Member

@amcasey amcasey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The protocol changes look good and I did a few other spot checks.

src/services/refactorProvider.ts Outdated Show resolved Hide resolved
* Checks if string "known" begins with string "requested".
* Used to match requested refactorKinds with a known refactorKind.
*/
export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, this doesn't require matching full hierarchy levels, right? So I can get all the extractions with "refactor.e"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. I experimented with something fancier but it didn't seem to add anything except code complexity.

it would be sorta nice if we could provide string completions for the refactor kinds, but I couldn't think of anything besides just listing them all out in a big union in the refactorKind type.

const extractToTypeAliasAction = {
name: "Extract to type alias",
description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias),
refactorKind: "refactor.extract.type",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it redundant to have every refactorKind start with "refactor."?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an LSP naming convention that VSCode also tries to follow for it's keybinding names:
https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_codeAction

It felt premature to chop off the "refactor" bit without knowing how our codeAction routing will ultimately look.

src/services/refactors/moveToNewFile.ts Outdated Show resolved Hide resolved
@jessetrinity jessetrinity merged commit 8bbef81 into microsoft:master Dec 23, 2020
mjbvz added a commit to microsoft/vscode that referenced this pull request Jan 5, 2021
mjbvz added a commit to microsoft/vscode that referenced this pull request Jan 5, 2021
* Add support for TS's Hierarchical refactorings API

microsoft/TypeScript#41975

* Add simple browser extension

This change adds a new 'simple browser' extension. This extension uses a webview to render webpages directly in VS Code. We plan on using it for optionally previewing local servers in both desktop and codespaces

The browser itself has a number of limitations due to the security around iframes:

- It traps keyboard focus
- We can't detect if a page fails to load
- We can't track the current url of the iframe

* Add experimental alert when the iframe is focused

* Disable events on focus warning

* Hooking up simple browser to opener
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TSServer: Refactor trigger reason
5 participants